// 整体功能
// 该代码实现了一个基于点对点网络的聊天应用，结合了多种协议和功能，
// 支持局域网内节点自动发现、分布式节点发现和存储、消息的可靠传输以及节点身份验证等功能。
// 如何运行，启用 mDNS
// 要运行该代码，需要设置环境变量 CHAT_P2P_MDNS_ENABLE 为 true，然后运行命令：
// export CHAT_P2P_MDNS_ENABLE=true && cargo run --example p2p_chat_with_gossipsub
// 运行其他节点
// export CHAT_P2P_MDNS_ENABLE=true && cargo run --example p2p_chat_with_gossipsub
use std::{
    hash::{DefaultHasher, Hash, Hasher},
    time::{Duration, SystemTime, UNIX_EPOCH},
};

// 引入 anyhow 的错误处理库，用于简化错误处理。anyhow 提供统一的错误类型和便捷的错误包装方法，
// 让代码中的错误处理更加简洁，减少样板代码。
#[allow(unused)]
use anyhow::{Context, anyhow};

// 引入 libp2p 相关模块，libp2p 是用于构建点对点网络应用的库，包含多种网络协议和工具。
use libp2p::{
    // Multiaddr 用于表示多地址，可包含多种传输协议的地址信息，如 TCP、UDP 等。
    // StreamProtocol 用于定义流协议，在网络通信中标识不同的协议。
    // SwarmBuilder 是构建 Swarm 实例的构建器，Swarm 是 libp2p 的核心管理结构。
    Multiaddr,
    StreamProtocol,
    SwarmBuilder,
    // autonat 模块用于自动处理 NAT 穿透，帮助节点在 NAT 环境下建立外部连接。
    autonat,
    // dcutr 模块用于处理数据中心间的传输，优化跨数据中心的连接。
    dcutr,
    // 引入 futures 库的 StreamExt 特征，为异步流提供扩展方法，方便处理异步事件。
    futures::StreamExt,
    // gossipsub 模块实现了 GossipSub 协议，用于发布和订阅消息，支持高效的消息传播。
    gossipsub::{self, IdentTopic},
    // identify 模块提供节点身份验证功能，节点可交换自身的身份信息。
    identify,
    // kad 模块实现了 Kademlia 分布式哈希表协议，用于分布式节点发现和存储。
    // Mode 枚举定义 Kademlia 协议的不同模式，如客户端、服务器等。
    // MemoryStore 是内存存储实现，用于存储 Kademlia 协议的数据。
    kad::{self, Mode, store::MemoryStore},
    // mdns 模块实现了多播 DNS 协议，可在局域网内自动发现其他节点。
    mdns,
    // Protocol 枚举定义多地址中的协议类型，如 P2p、Tcp 等。
    multiaddr::Protocol,
    // noise 模块实现了 Noise 加密协议，为网络通信提供端到端的加密。
    noise,
    // ping 模块实现了 Ping 协议，定期发送 Ping 消息保持连接活跃。
    ping,
    // relay 模块提供中继功能，帮助节点在无法直接连接时通过中继服务器通信。
    relay,
    // request_response 模块提供请求 - 响应模式的通信机制，
    // json 模块是基于 JSON 序列化的请求 - 响应实现。
    request_response::{self, json},
    // NetworkBehaviour 是特征，用于定义网络行为的组合。
    // SwarmEvent 枚举表示 Swarm 实例可能产生的各种事件。
    // toggle 模块提供 Toggle 类型，可动态启用或禁用某种网络行为。
    swarm::{NetworkBehaviour, SwarmEvent, behaviour::toggle},
    // tcp 模块实现 TCP 传输协议。
    // yamux 模块实现 Yamux 多路复用协议，在单个连接上复用多个流。
    tcp,
    yamux,
};
// 引入 serde 库的 Deserialize 和 Serialize 特征，用于实现数据的序列化和反序列化，
// 方便在网络通信中传输数据。
use serde::{Deserialize, Serialize};
// 引入 tokio 库的异步 IO 相关模块，用于实现异步的标准输入读取。
// AsyncBufReadExt 为异步缓冲读取器提供扩展方法，BufReader 是异步缓冲读取器。
use tokio::io::{self, AsyncBufReadExt, BufReader};

// 定义消息请求结构体，用于发送聊天消息。
// 使用 #[derive(Debug, Clone, Serialize, Deserialize)] 自动派生调试信息打印、克隆、序列化和反序列化功能。
#[derive(Debug, Clone, Serialize, Deserialize)]
struct MessageRequest {
    // 消息内容字段，使用 String 类型存储文本。
    pub message: String,
}

// 定义消息响应结构体，用于确认消息接收。
// 使用 #[derive(Debug, Clone, Serialize, Deserialize)] 自动派生调试信息打印、克隆、序列化和反序列化功能。
#[derive(Debug, Clone, Serialize, Deserialize)]
struct MessageResponse {
    // 确认标志，true 表示消息成功接收。
    pub ack: bool,
}

// 定义网络行为组合，使用 #[derive(NetworkBehaviour)] 自动实现 NetworkBehaviour 特征。
// 该结构体将多种网络协议行为组合在一起，方便统一管理和处理。
#[derive(NetworkBehaviour)]
struct ChatBehavior {
    // Ping 行为，定期发送 Ping 消息来保持连接的活跃状态。
    ping: ping::Behaviour,
    // 消息行为，使用 JSON 序列化处理消息的请求和响应。
    messaging: json::Behaviour<MessageRequest, MessageResponse>,
    // mDNS 行为，用于在局域网内发现和连接其他节点。
    // 使用 toggle::Toggle 包装，可通过环境变量动态启用或禁用 mDNS 功能。
    mdns: toggle::Toggle<mdns::Behaviour<mdns::tokio::Tokio>>,
    // 身份验证行为，用于验证节点的身份信息。
    identify: identify::Behaviour,
    // Kademlia 协议行为，用于分布式节点发现和存储。
    kademlia: kad::Behaviour<MemoryStore>,
    // 自动 NAT 行为，用于处理 NAT 穿透，自动配置端口映射。
    autonat: autonat::Behaviour,
    // Relay 客户端行为，用于连接到 Relay 服务器。
    relay_client: relay::client::Behaviour,
    // Relay 服务器行为，用于接收 Relay 客户端的连接。
    relay_server: relay::Behaviour,
    // DCUTR 行为，用于处理数据中心间的传输。
    dcutr: dcutr::Behaviour,
    // GossipSub 行为，用于发布和订阅消息。
    gossipsub: gossipsub::Behaviour,
}

// 定义聊天主题常量，用于 GossipSub 协议的消息发布和订阅。
const CHAT_TOPIC: &str = "chat";

// 使用 tokio 运行时，将 main 函数作为异步函数执行。
#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // 定义聊天主题，使用 IdentTopic 类型。
    let chat_topic: IdentTopic = IdentTopic::new(CHAT_TOPIC);
    // 从环境变量 CHAT_P2P_MDNS_ENABLE 中获取是否启用 mDNS 的标志，
    // 并将其解析为布尔类型。若环境变量未设置或解析失败，将返回错误。
    let mdns_enable: bool = std::env::var("CHAT_P2P_MDNS_ENABLE")?.parse()?;
    // 从环境变量 CHAT_P2P_BOOTSTRAP_PEERS 中获取引导节点地址列表，
    // 将其按逗号分割并转换为字符串向量。若环境变量未设置，将返回错误。
    let bootstrap_peers = std::env::var("CHAT_P2P_BOOTSTRAP_PEERS").map(|peers| {
        peers
            .split(",")
            .map(|peer| peer.to_string())
            .collect::<Vec<String>>()
    });

    // 构建 Swarm 实例，Swarm 是 libp2p 的核心管理结构，负责管理网络连接、事件处理等。
    let noise_config = noise::Config::new;
    // Noise 加密配置。
    let yamux_config = yamux::Config::default;
    // Yamux 多路复用默认配置。
    let tcp_config = tcp::Config::default();
    // TCP 传输默认配置。
    let mut swarm = SwarmBuilder::with_new_identity()
        // 生成新的身份信息，包括密钥对和对应的 PeerID。
        .with_tokio()
        // 使用 tokio 异步运行时，处理网络事件和异步任务。
        .with_tcp(
            // 配置 TCP 传输层。
            tcp_config,
            // 使用默认的 TCP 配置。
            noise_config,
            // 使用 Noise 加密协议，保证通信的安全性。
            yamux_config,
            // 使用默认的 Yamux 多路复用配置，在单个连接上复用多个流。
        )?
        .with_relay_client(noise_config, yamux_config)?
        // 配置 Relay 客户端，使用 Noise 加密和 Yamux 多路复用。
        .with_behaviour(|key_pair, relay_client| {
            // 配置网络行为。
            let mdns = if mdns_enable {
                // 如果启用 mDNS，则创建 mDNS 行为实例。
                toggle::Toggle::from(Some(mdns::Behaviour::new(
                    mdns::Config::default(),
                    // 使用默认的 mDNS 配置。
                    key_pair.public().to_peer_id(),
                    // 使用节点的公钥生成对应的 PeerID。
                )?))
            } else {
                // 如果禁用 mDNS，则创建一个空的 Toggle 实例。
                toggle::Toggle::from(None)
            };
            // 获取节点的 PeerID。
            let peer_id = key_pair.public().to_peer_id();
            // 创建 Kademlia 协议配置实例，指定协议标识符。
            let mut kad_config = kad::Config::new(StreamProtocol::new("/awesome-chat/kad/1.0.0"));
            // 设置定期引导间隔为 10 秒，定期尝试连接引导节点，更新节点路由表。
            kad_config.set_periodic_bootstrap_interval(Some(Duration::from_secs(10)));

            // 配置 GossipSub 协议的参数。
            let gossipsub_config = gossipsub::ConfigBuilder::default()
                // 设置心跳间隔为 10 秒，定期维护消息传播网络。
                .heartbeat_interval(Duration::from_secs(10))
                // 设置消息验证模式为严格模式，确保消息的合法性。
                .validation_mode(gossipsub::ValidationMode::Strict)
                // 自定义消息 ID 生成函数，确保消息 ID 的唯一性。
                .message_id_fn(|message| {
                    let mut hasher = DefaultHasher::new();
                    // 使用消息数据、主题、来源 PeerID 和当前时间生成哈希值。
                    message.data.hash(&mut hasher);
                    message.topic.hash(&mut hasher);
                    if let Some(peer_id) = message.source {
                        peer_id.hash(&mut hasher);
                    };
                    let now = SystemTime::now()
                        .duration_since(UNIX_EPOCH)
                        .unwrap()
                        .as_millis();
                    now.to_string().hash(&mut hasher);
                    // 将哈希值转换为消息 ID。
                    gossipsub::MessageId::from(hasher.finish().to_string())
                })
                .build()?;

            // 配置 ChatBehavior 实例。
            Ok(ChatBehavior {
                ping: ping::Behaviour::new(
                    // 初始化 Ping 行为。
                    ping::Config::new()
                        // 使用默认的 Ping 配置。
                        .with_interval(Duration::from_secs(10)),
                    // 设置 Ping 消息的发送间隔为 10 秒。
                ),
                messaging: json::Behaviour::new(
                    // 初始化消息行为。
                    [(
                        // 定义自定义协议标识符。
                        StreamProtocol::new("/awesome-chat/1"),
                        // 支持完整的请求 - 响应模式。
                        request_response::ProtocolSupport::Full,
                    )],
                    request_response::Config::default(),
                    // 使用默认的请求 - 响应配置。
                ),
                // 使用前面创建的 mDNS 行为实例。
                mdns,
                identify: identify::Behaviour::new(identify::Config::new(
                    // 初始化身份验证行为。
                    "1.0.0".to_string(),
                    // 版本信息。
                    key_pair.public(),
                    // 节点的公钥。
                )),
                kademlia: kad::Behaviour::with_config(
                    // 初始化 Kademlia 协议行为。
                    peer_id,
                    // 节点的 PeerID。
                    MemoryStore::new(peer_id),
                    // 使用内存存储来存储 Kademlia 协议的数据。
                    kad_config,
                    // 使用前面创建的 Kademlia 协议配置。
                ),
                autonat: autonat::Behaviour::new(peer_id, autonat::Config::default()),
                // 初始化自动 NAT 行为，使用默认配置。
                relay_client,
                // 使用前面创建的 Relay 客户端行为实例。
                relay_server: relay::Behaviour::new(peer_id, relay::Config::default()),
                // 初始化 Relay 服务器行为，使用默认配置。
                dcutr: dcutr::Behaviour::new(peer_id),
                // 初始化 DCUTR 行为。
                gossipsub: gossipsub::Behaviour::new(
                    // 初始化 GossipSub 行为。
                    gossipsub::MessageAuthenticity::Signed(key_pair.clone()),
                    // 使用签名验证消息的真实性。
                    gossipsub_config,
                    // 使用前面创建的 GossipSub 配置。
                )?,
            })
        })?
        .with_swarm_config(|config| config.with_idle_connection_timeout(Duration::from_secs(30)))
        // 配置 Swarm 实例，设置空闲连接的超时时间为 30 秒，超过该时间将关闭连接。
        .build();
    // 完成 Swarm 实例的构建。

    // 开始监听随机可用的 TCP 端口，0 表示由操作系统自动分配端口。
    swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse()?)?;
    // 将 Kademlia 协议的模式设置为服务器模式。
    swarm.behaviour_mut().kademlia.set_mode(Some(Mode::Server));

    // 打印本地节点的 PeerID，方便用户查看。
    println!("Peer ID: {:?}", swarm.local_peer_id());

    if let Ok(bootstrap_peers) = bootstrap_peers {
        // 如果成功获取到引导节点地址列表。
        for bootstrap_peer in bootstrap_peers {
            // 遍历引导节点地址列表。
            // 将引导节点地址解析为 Multiaddr 类型。
            let addr: Multiaddr = bootstrap_peer.parse().unwrap();
            // 从地址中提取 PeerID。
            let peer_id = addr
                .iter()
                .map(|_addr_str| {
                    if let Some(Protocol::P2p(peer_id)) = addr.iter().last() {
                        return Some(peer_id);
                    }
                    None
                })
                .filter(Option::is_some)
                .last()
                .ok_or(anyhow!("No peer id found in address"))?
                .ok_or(anyhow!("Bootstrap peer address {bootstrap_peer} is wrong"))?;
            // 将引导节点的地址添加到 Kademlia 协议的路由表中。
            swarm.behaviour_mut().kademlia.add_address(&peer_id, addr);
            // 将引导节点添加到 GossipSub 协议的显式对等节点列表中。
            swarm.behaviour_mut().gossipsub.add_explicit_peer(&peer_id);
        }
    }

    // 订阅聊天主题，开始接收该主题下的消息。
    swarm.behaviour_mut().gossipsub.subscribe(&chat_topic)?;
    // 创建一个异步缓冲读取器，用于从标准输入读取用户输入的消息。
    let mut stdin = BufReader::new(io::stdin()).lines();

    // 主事件循环，不断监听网络事件和用户输入。
    loop {
        tokio::select! {
            // 处理网络事件。
            event = swarm.select_next_some() => match event {
                // 当有新的监听地址时触发该事件。
                SwarmEvent::NewListenAddr { address, .. } => {
                    println!("Listening on {:?}", address);
                }
                // 当与其他节点建立连接成功时触发该事件。
                SwarmEvent::ConnectionEstablished { peer_id, .. } => {
                    println!("Established connection to peer: {:?}", peer_id);
                },
                // 当接收到网络行为相关的事件时触发该事件。
                SwarmEvent::Behaviour(event) => match event {
                    // 处理 Ping 协议事件。
                    ChatBehaviorEvent::Ping(event) => {
                        println!("Received ping: {:?}", event);
                    }
                    // 处理消息协议事件。
                    ChatBehaviorEvent::Messaging(event) => match event {
                        request_response::Event::Message {
                            peer, connection_id, message
                        } => match message {
                            // 处理接收到的消息请求。
                            request_response::Message::Request { request_id, request, channel } => {
                                println!("Received request from peer: {:?}, connection_id: {:?}, request_id: {:?}, request: {:?}",
                                    peer, connection_id, request_id, request);
                                // 构造确认响应。
                                let response = MessageResponse { ack: true };
                                // 发送响应消息。
                                if let Err(e) = swarm
                                    .behaviour_mut()
                                    .messaging
                                    .send_response(channel, response) {
                                        println!("Error sending response: {:?}", e);
                                    }
                            },
                            // 处理接收到的消息响应。
                            request_response::Message::Response { request_id, response,.. } => {
                                println!("Received response from peer: {:?}, connection_id: {:?}, request_id: {:?}, response: {:?}",
                                    peer, connection_id, request_id, response);
                            }
                        },
                        // 其他消息协议事件，此处简化处理，不做具体操作。
                        _ => {}
                    },
                    // 处理 mDNS 服务发现事件。
                    ChatBehaviorEvent::Mdns(event) => match event {
                        // 当发现新的节点时触发该事件。
                        mdns::Event::Discovered(new_peers) => {
                            // 遍历所有发现的节点。
                            for (peer_id, addr) in new_peers {
                                println!("Discovered peer: {:?} at {:?}", peer_id, addr);
                                // 尝试连接发现的节点。
                                if let Err(e) = swarm.dial(peer_id) {
                                    println!("Error dialing peer: {:?}", e);
                                    continue;
                                }
                                // 将节点地址添加到 Swarm 实例中。
                                swarm.add_peer_address(peer_id, addr);
                            }
                        }
                        // 当节点信息过期时触发该事件，此处不做具体操作。
                        mdns::Event::Expired(_) => {}
                    },
                    // 处理身份验证事件。
                    ChatBehaviorEvent::Identify(event) => match event {
                        // 当接收到其他节点的身份信息时触发该事件。
                        identify::Event::Received { connection_id, peer_id, info } => {
                            println!("Received identify from {:?} ({:?}): {:?}", peer_id, connection_id, info);
                            // 将其他节点的监听地址添加到 Kademlia 协议的路由表中。
                            for addr in info.listen_addrs {
                                swarm.behaviour_mut().kademlia.add_address(&peer_id, addr);
                            }
                        },
                        // 当成功发送身份信息到其他节点时触发该事件。
                        identify::Event::Sent { connection_id, peer_id } => {
                            println!("Sent identify to {:?} ({:?})", peer_id, connection_id);
                        }
                        // 当推送身份信息到其他节点时触发该事件。
                        identify::Event::Pushed { connection_id, peer_id, info } => {
                            println!("Pushed identify to {:?} ({:?}): {:?}", peer_id, connection_id, info);
                        }
                        // 当身份验证过程中出现错误时触发该事件。
                        identify::Event::Error { connection_id, peer_id, error } => {
                            println!("Error: {:?} from {:?} ({:?})", error, peer_id, connection_id);
                        }
                    },
                    // 处理 Kademlia 协议事件。
                    ChatBehaviorEvent::Kademlia(event) => match event {
                        // 处理接收到的入站请求事件，此处不做具体操作。
                        kad::Event::InboundRequest { .. } => {},
                        // 处理出站查询进度事件，此处不做具体操作。
                        kad::Event::OutboundQueryProgressed {..} => {},
                        // 当路由表更新时触发该事件。
                        kad::Event::RoutingUpdated { peer, is_new_peer, addresses, bucket_range, old_peer } => {
                            println!("Routing updated: peer: {:?}, is_new_peer: {:?}, addresses: {:?}, bucket_range: {:?}, old_peer: {:?}", peer, is_new_peer, addresses, bucket_range, old_peer);
                            // 尝试连接路由表中更新的节点地址。
                            addresses.iter().for_each(|addr| {
                                if let Err(e) = swarm.dial(addr.clone()) {
                                    println!("Error dialing peer: {:?}", e);
                                }
                            })
                        },
                        // 处理不可路由节点事件，此处不做具体操作。
                        kad::Event::UnroutablePeer {..} => {},
                        // 处理可路由节点事件，此处不做具体操作。
                        kad::Event::RoutablePeer {..} => {},
                        // 处理待路由节点事件，此处不做具体操作。
                        kad::Event::PendingRoutablePeer {..} => {},
                        // 处理模式变更事件，此处不做具体操作。
                        kad::Event::ModeChanged {..} => {},
                    },
                    // 自动NAT事件
                    ChatBehaviorEvent::Autonat(event) => match event {
                        autonat::Event::StatusChanged { old, new } => {
                            println!("Autonat status changed: {:?} -> {:?}", old, new);
                        },
                        autonat::Event::InboundProbe(event) => {
                            println!("Inbound probe event: {:?}", event);
                        },
                        autonat::Event::OutboundProbe(event) =>  {
                            println!("Outbound probe event: {:?}", event);
                        }
                    },
                    // Relay客户端事件
                    ChatBehaviorEvent::RelayClient(event) => {
                        println!("Relay client event: {:?}", event);
                    },
                    // Relay服务器事件
                    ChatBehaviorEvent::RelayServer(event) => {
                        println!("Relay server event: {:?}", event);
                    },
                    // DCUTR事件
                    ChatBehaviorEvent::Dcutr(event) => {
                        println!("DCUTR event: {:?}", event);
                    }
                    // 处理 GossipSub 协议事件。
                    ChatBehaviorEvent::Gossipsub(event) => {
                        // 当接收到订阅主题的消息时触发该事件。
                        println!("Received message from topic: {:?}", event);
                    }
                }
                // 其他网络事件，此处简化处理，不做具体操作。
                _ => {}
            },

            // 处理用户输入事件。
            Ok(Some(line)) = stdin.next_line() => {
                swarm.behaviour_mut().gossipsub.publish(chat_topic.clone(), line.as_bytes())?;
                // 打印本地发送的消息。
                println!("{}: {:?}", swarm.local_peer_id(), line);
            }
        }
    }
}
